﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "ActionWallTag.h"
#include "IUiDocument.h"
#include "IDocument.h"
#include "IUiView.h"
#include "IUserTransaction.h"
#include "../UIPlatform/CommandIds.h"
#include "CommandIds.h"
#include "IElementModelShape.h"
#include "IGraphicsElementShape.h"
#include "IPickPointAction.h"
#include "Vector3dUtils.h"
#include "UiCommonDialog.h"
#include "../WallModule/Wall/Wall.h"
#include "WallTag\WallTag.h"
#include "ISnap.h"
#include "IGraphicsNodeReference.h"
#include "ISnapContext.h"
#include "IModelView.h"
#include "GcmpBuiltInCategoryUniIdentities.h"
#include "IGraphicsStyleManager.h"
#include "IGraphicsBRepBody.h"
#include "ICategory.h"
#include "GraphicsNodeUtils.h"
#include "ICategoryLibrary.h"
#include "IRegenerator.h"
#include "ISnapSettings.h"
#include "ICommandManager.h"
#include "IPickPointActionConfig.h"
#include "UiDocumentViewUtils.h"
#include "IGenericElement.h"
#include "IElementBasicInformation.h"
#include "CategoryDefine.h"
#include "InteractivePicker.h"
#include "IPickNodeReferenceAction.h"
#include "IPickFilter.h"
#include "IPickTarget.h"
#include "ISelection.h"
#include "IHighlights.h"
#include "IGenericElement.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 
using namespace Sample;
using namespace gcmp;

namespace {
    bool GetCustomMousePath(std::wstring& cursorPath)
    {
        cursorPath = L":/images/Cursor/cross.png";
        return true;
    }
    gcmp::Vector3d GetTopMiddlePoint(IDocument* pDocument, gcmp::ElementId wallId)
    {
        Vector3d referenceLineStartPoint = Vector3d(0, 0, 0);
        DBG_WARN_AND_RETURN_UNLESS(pDocument, referenceLineStartPoint, L"获取Document失败",L"GDMP",L"2023-12-30");
        IGenericElement* pGenericElement = quick_cast<IGenericElement>(pDocument->GetElement(wallId));
        DBG_WARN_AND_RETURN_UNLESS(pGenericElement, referenceLineStartPoint, L"获取GenericElement失败",L"GDMP",L"2023-12-30");
        Wall* pWall = quick_cast<Wall>(pGenericElement->GetExternalObject());
        DBG_WARN_AND_RETURN_UNLESS(pWall, referenceLineStartPoint, L"获取Wall失败",L"GDMP",L"2023-12-30");
        IElement* pWallElement = pWall->GetOwnerElement();
        DBG_WARN_AND_RETURN_UNLESS(pWallElement, referenceLineStartPoint, L"Element不能为空",L"GDMP",L"2023-12-30");
        IElementModelShape* pElementModelShape = pWallElement->GetElementModelShape();
        DBG_WARN_AND_RETURN_UNLESS(pElementModelShape, referenceLineStartPoint, L"ElementModelShape不能为空",L"GDMP",L"2023-12-30");
        const IGraphicsElementShape* pGraphicsElementShape = pElementModelShape->GetGraphicsElementShape();
        DBG_WARN_AND_RETURN_UNLESS(pGraphicsElementShape, referenceLineStartPoint, L"获取GraphicsElementShape失败",L"GDMP",L"2023-12-30");
        Box3d box = pGraphicsElementShape->GetBox();
        referenceLineStartPoint = box.GetCenter() + Vector3d::Create(0, 0, (box.GetMaxPoint().Z() - box.GetMinPoint().Z()) / 2);
        return referenceLineStartPoint;
    }
    bool IsNeedDimensionToWall(const gcmp::ElementId& id)
    {
        IDocument* pDocument = UiDocumentViewUtils::GetCurrentDocument();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pDocument, L"pDocument为空",L"GDMP",L"2023-12-30");
        gcmp::IGenericElement* pSelElement = quick_cast<IGenericElement>(pDocument->GetElement(id));
        if (pSelElement == nullptr)
            return false;
        IElementBasicInformation* pElementBasicInformation = pSelElement->GetBasicInformation();
        if (pElementBasicInformation == nullptr)
            return false;
        if (pElementBasicInformation->GetCategoryUid() != Sample::WALL_CATEGORY)
            return false;
        Wall* pWall = quick_cast<Wall>(pSelElement->GetExternalObject());
        if (pWall == nullptr)
            return false;
        ElementId wallTagId = pWall->GetWallTagId();
        if (pDocument->GetElement(wallTagId))//L"该墙已经有标注"
            return false;
        return true;
    }
    class PickWallFilter : public gcmp::IPickFilter
    {
    public:

        virtual bool AllowElement(const gcmp::ElementId& id) const override
        {
            return IsNeedDimensionToWall(id);
        }

        virtual bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const override
        {
            return true;
        }

        virtual bool SetPickTargetOption(gcmp::IPickTarget* pickTarget) override
        {
            pickTarget->DisableAll();
            pickTarget->EnableGraphicsElementShape();
            return true;
        }
    };
}

namespace Sample
{
    class WallTagPickConfigure : public gcmp::IPickPointActionConfig
    {
        DEFINE_CAST_DERIVED(WallTagPickConfigure, gcmp::IPickPointActionConfig);
    public:
        WallTagPickConfigure(ActionWallTag* pAction) :m_ParentAction(pAction) {};
        virtual ~WallTagPickConfigure() {};

        // 用户配置自定义的正交模式
        virtual bool ConfigSnapSettings(ISnapSettings* snapSetting) override
        {
            if (snapSetting)
            {
                snapSetting->SetIsForceHorizontalOrVertical(m_ParentAction->GetCtrlKeyPressed());
            }
            return false;
        };

    private:
        ActionWallTag* m_ParentAction;
    };
}


ActionWallTag::ActionWallTag()
    : m_drawStatus(eWait)
    , m_isUserCancelled(false)
    , m_opsnapStart(nullptr)
    , m_opsnapEnd(nullptr)
{
    ISnapSettings* opSetting = ISnapSettings::GetGlobalSnapSettings();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opSetting, L"opSetting获取失败",L"GDMP",L"2023-12-30");
    m_opOldSetting = opSetting->Clone();
}

ActionWallTag::~ActionWallTag()
{
    ISnapSettings* opSetting = ISnapSettings::GetGlobalSnapSettings();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opSetting, L"opSetting获取失败",L"GDMP",L"2023-12-30");
    if (m_opOldSetting.get())
    {
        opSetting->SetSnapSettings(m_opOldSetting.get());
    }
}

void ActionWallTag::OnChildActionFinished(IUiView* pCurrentView, const ActionOutput& childActionReturnParam)
{
    gcmp::IDocument* pDoc = GetDoc();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"文档为空",L"GDMP",L"2023-12-30");
    if (m_drawStatus == eGetting_Wall)
    {
        if (m_isUserCancelled || m_gNodeReferencesSelection.empty())
        {
            ISelection::Get()->Clear(pDoc);
            IHighlights::Get()->Clear();
            // 如果用户在选择墙时退出，就退出整个工具
            CleanupTempGraphicsShape();
            MarkFinishStatus(ActionFinishStatus::Cancelled);
            UpdateView();
            return;
        }
        else
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(m_gNodeReferencesSelection[0], L"m_gNodeReferencesSelection[0]为空",L"GDMP",L"2023-12-30");
            ElementId wallId = m_gNodeReferencesSelection[0]->GetElementId();
            m_ptReferenceStartPoint = GetTopMiddlePoint(pDoc, wallId);
            EnterGettingEndPointState(pCurrentView);
        }
    }
    else if (m_drawStatus == eGetting_EndPoint)
    {
        CleanupTempGraphicsShape();
        m_pickedPoints.clear();
        if (m_isUserCancelled)
        {
            // 如果用户在画第二个点时选择退出，那么先退到选墙的状态
            EnterGettingWallState(pCurrentView);
            UpdateView();
            return;
        }
        else if (Vector3dUtils::IsEqual(m_ptReferenceStartPoint, m_ptReferenceEndPoint))
        {
            UiCommonDialog::ShowMessageBox(GBMP_TR(L"绘制墙体"), GBMP_TR(L"起始点和终止点不能相同"), (int)UiCommonDialog::ButtonType::OK);
            EnterGettingEndPointState(pCurrentView);
        }
        else
        {
            OnDrawFinished(pCurrentView);                               
            EnterGettingWallState(pCurrentView);            
        }
    }
    UpdateView();
}

void ActionWallTag::EnterGettingWallState(gcmp::IUiView* pCurrentView)
{
    m_gNodeReferencesSelection.clear();
    m_drawStatus = eGetting_Wall;
    bool bSingleSelectModeInput = true;
    PickNodeReferenceExchangeData exchangeData(bSingleSelectModeInput, &m_gNodeReferencesSelection, &m_isUserCancelled, &m_currentMousePt);
    PickNodeReferenceOption pickOptions;
    pickOptions.SetPickPixelTolerance(10);
    pickOptions.SetMultiSelect(false);
    pickOptions.SetMouseTooltipMessage(L"选择墙体");
    pickOptions.SetFinishedMode(ActionFinishedMode::LButtonDown);
    pickOptions.SetIsPickingHighlightOnlyGraphicsNodeAllowed(true);
    OwnerPtr<IPickFilter> opPickFilter = NEW_AS_OWNER_PTR(PickWallFilter);
    DBG_WARN_AND_RETURN_VOID_UNLESS(opPickFilter, L"创建墙体拾取过滤器失败",L"GDMP",L"2023-12-30");
    gcmp::OwnerPtr<IAction> opPickNodeReferenceAction = IPickNodeReferenceAction::Create(exchangeData, TransferOwnership(opPickFilter), pickOptions);
    DBG_WARN_AND_RETURN_VOID_UNLESS(opPickNodeReferenceAction, L"创建拾取墙体Action失败",L"GDMP",L"2023-12-30");
    {
        ActionBase* pActionBase = dynamic_cast<ActionBase*>(opPickNodeReferenceAction.get());
        DBG_WARN_AND_RETURN_VOID_UNLESS(pActionBase, L"pActionBase获取失败",L"GDMP",L"2023-12-30");
        pActionBase->SetCursorType(CursorType::CrossHairCursor);
    }
    StartChildAction(TransferOwnership(opPickNodeReferenceAction));
}

void ActionWallTag::EnterGettingEndPointState(gcmp::IUiView* pCurrentView)
{
    m_drawStatus = eGetting_EndPoint;
    m_isUserCancelled = false;
    m_opsnapStart = nullptr;
    SetPromptMessage(GBMP_TR(L"请输入墙体标注引导线终止点"));
    OwnerPtr<IPickPointActionConfig> opWallPickConfigure = NEW_AS_OWNER_PTR(WallTagPickConfigure, this);
    //设置三维点捕捉
    PickPointExchangeData exchangeData(&m_ptReferenceEndPoint, &m_isUserCancelled, &m_pickedPoints, &m_opsnapStart, nullptr);
    gcmp::OwnerPtr<IPickPointAction> opPickPointAction = IPickPointAction::Create(exchangeData,
        IPickPointAction::CreateMoveCallback(&ActionWallTag::OnPointUpdate, this),
        ActionFinishedMode::LButtonDown, false, TransferOwnership(opWallPickConfigure));
    DBG_WARN_AND_RETURN_VOID_UNLESS(opPickPointAction, L"PickPointAction创建失败",L"GDMP",L"2023-12-30");
    ISnapContext* opContext = opPickPointAction->GetSnapContext();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opContext, L"SnapContext创建失败",L"GDMP",L"2023-12-30");
    ISnapSettings* opSetting = ISnapSettings::GetGlobalSnapSettings();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opSetting, L"SnapSettings创建失败",L"GDMP",L"2023-12-30");
    opSetting->SetCanSnap3dPoint(true);
    opSetting->SetCanSnap3DAxialDirectionLine(true);
    {
        //正交必须设置项
        opSetting->SetCanSnapHorizontalVerticalLine(true);
        opSetting->SetIsForceHorizontalOrVertical(true);
        opSetting->SetCanSnapLength(true);
    }
     opSetting->SetCanSnap3DAxialDirectionLine(false);
    {
        //延伸捕捉
        opSetting->SetIsRemoteSnapOff(false);
        opSetting->SetCanExtensionSnap(true);
        opSetting->SetCanSnap3DExtension(true);
    }
    opContext->SetSnapSettings(opSetting);
    opPickPointAction->SetSnapContext(opContext);
    {
        //自定义光标
        opPickPointAction->SetCursorType(CursorType::Custom);
        opPickPointAction->SetCursorPathCallBack(GetCustomMousePath);
    }

    opPickPointAction->SetFirstPoint(m_ptReferenceStartPoint);
    StartChildAction(TransferOwnership(opPickPointAction));
}

void ActionWallTag::OnPointUpdate(gcmp::IUiView* pCurrentView, const gcmp::Vector3d& pickedPt, const gcmp::Vector3d& mousePos)
{
    CleanupTempGraphicsShape();
    if (Vector3dUtils::IsEqual(m_ptReferenceStartPoint, pickedPt))
        return;
    DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView, L"pCurrentView为空",L"GDMP",L"2023-12-30");
    OwnerPtr<IGraphicsElementShape> opGraphicsElementShape = OnWallTagUpdate(pCurrentView, pickedPt);
    DrawTempGraphicsElementShape(TransferOwnership(opGraphicsElementShape));
    IUiDocument* pUiDocument= pCurrentView->GetUiDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pUiDocument, L"pUiDocument为空",L"GDMP",L"2023-12-30");
    pUiDocument->UpdateView();
}

void ActionWallTag::InitAction(gcmp::IUiView* pCurrentView)
{
    const GraphicsNodeReferenceOwnerPtrSet& selectedElems = ISelection::Get()->GetGraphicsNodeReferences();

    if (!selectedElems.empty())
    {
        FOR_EACH(item, selectedElems)
        {
            ElementId elemId = item->GetElementId();
            if (IsNeedDimensionToWall(elemId))
            {
                m_gNodeReferencesSelection.emplace_back(TransferOwnership(item->Clone()));
                m_drawStatus = eGetting_Wall;
                IDocument* pDocument = UiDocumentViewUtils::GetCurrentDocument();
                DBG_WARN_AND_RETURN_VOID_UNLESS(pDocument, L"pDocument为空",L"GDMP",L"2023-12-30");
                m_ptReferenceStartPoint = GetTopMiddlePoint(pDocument, elemId);
                EnterGettingEndPointState(pCurrentView);
                ISelection::Get()->Clear(pDocument);
                return;
            }
        }
        EnterGettingWallState(pCurrentView);
    }
    else
        EnterGettingWallState(pCurrentView);
}

OwnerPtr<IGraphicsElementShape> ActionWallTag::OnWallTagUpdate(gcmp::IUiView* pCurrentView, const gcmp::Vector3d& endPt)
{
    gcmp::IDocument* pDoc = GetDoc();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"文档为空",L"GDMP",L"2023-12-30");
    OwnerPtr<IUserTransaction> opUserTransaction =IUserTransaction::Create(pDoc, GBMP_TR(L"绘制墙体")); 
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opUserTransaction, L"opUserTransaction创建失败",L"GDMP",L"2023-12-30");
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_gNodeReferencesSelection[0], L"m_gNodeReferencesSelection[0]为空",L"GDMP",L"2023-12-30");
    WallTag* pWallTag = WallTag::Create(pDoc, endPt- m_ptReferenceStartPoint, m_gNodeReferencesSelection[0]->GetElementId(), ElementCreationOptions::Transient);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pWallTag, L"创建墙体标注失败",L"GDMP",L"2023-12-30");
    IRegenerator* pRegenerator = pDoc->GetRegenerator();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pRegenerator, L"pRegenerator为空",L"GDMP",L"2023-12-30");
    pRegenerator->RegenerateElement(pWallTag->GetOwnerElementId());
    IElement* pElement = pWallTag->GetOwnerElement();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElement, L"pElement不能为空",L"GDMP",L"2023-12-30");
    OwnerPtr<IGraphicsElementShape> opGraphicsElementShape = pElement->GetElementModelShape()->TransferGraphicsElementShape();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opGraphicsElementShape, L"opGraphicsElementShape不能为空",L"GDMP",L"2023-12-30");
    pDoc->DeleteElement(pElement->GetElementId());
    opGraphicsElementShape->SetIsSelectable(false);
    opGraphicsElementShape->SetIsSnappable(false);
    opGraphicsElementShape->SetGraphicsStyleId(pDoc->GetGraphicsStyleManager()->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_TEMP_STYLE));
    GraphicsNodeUtils::CleanGraphicsNodeStyleAndMaterial(pDoc, opGraphicsElementShape.get());
    opUserTransaction->Rollback();
    return TransferOwnership(opGraphicsElementShape);
}

void ActionWallTag::OnDrawFinished(gcmp::IUiView* pCurrentView)
{
    gcmp::IDocument* pDoc = GetDoc();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"文档为空",L"GDMP",L"2023-12-30");
    OwnerPtr<IUserTransaction> opUserTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"绘制墙体标注"));
    DBG_WARN_AND_RETURN_VOID_UNLESS(opUserTransaction, L"opUserTransaction创建失败",L"GDMP",L"2023-12-30");
    DBG_WARN_AND_RETURN_VOID_UNLESS(m_gNodeReferencesSelection[0], L"m_gNodeReferencesSelection[0]为空",L"GDMP",L"2023-12-30");
    WallTag* pWallTag = WallTag::Create(pDoc, m_ptReferenceEndPoint - m_ptReferenceStartPoint, m_gNodeReferencesSelection[0]->GetElementId());
    DBG_WARN_UNLESS(pWallTag, L"pWallTag不能为空",L"GDMP",L"2023-12-30");
    ElementId wallId = m_gNodeReferencesSelection[0]->GetElementId();
    IGenericElement* pGenericElement = quick_cast<IGenericElement>(pDoc->GetElement(wallId));
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGenericElement, L"获取GenericElement失败",L"GDMP",L"2023-12-30");
    Wall* pWall = quick_cast<Wall>(pGenericElement->GetExternalObject());
    DBG_WARN_AND_RETURN_VOID_UNLESS(pWall, L"获取Wall失败",L"GDMP",L"2023-12-30");
    pWall->SetWallTagId(pWallTag->GetOwnerElementId());
    opUserTransaction->Commit();
}



bool ActionWallTag::PreviewKeyEvent()
{
    return true;
}

bool ActionWallTag::GetCtrlKeyPressed()
{
    return this->IsKeyAndButtonPressed(VK_CONTROL);
}

